using System;
using System.Xml;
using System.Text;
using Microsoft.ContentManagement.Publishing;
using Microsoft.ContentManagement.Publishing.Extensions.Placeholders;
using Microsoft.ContentManagement.Publishing.Extensions.Placeholders.Office;

namespace TemplateManager
{
	public class TemplateExport
	{
		private static void WriteElement(XmlTextWriter xtw, string name, string val)
		{
			xtw.WriteStartElement(name);
			xtw.WriteString(val);
			xtw.WriteEndElement();
		}

		#region Entry point

		public static void ExportTemplates(string path, bool isGallery, string filename)
		{
			// Open XML writer and write prolog
			XmlTextWriter xtw = new XmlTextWriter(filename, Encoding.UTF8);
			
			xtw.WriteStartDocument();

			if (isGallery)
			{
				xtw.WriteStartElement("TemplateGalleryExport");
				WriteGallery(xtw, path);
				xtw.WriteEndElement();
			}
			else
			{
				xtw.WriteStartElement("TemplateExport");
				WriteTemplate(xtw, path);
				xtw.WriteEndElement();
			}

			xtw.Close();
		}

		#endregion

		private static void WriteGallery(XmlTextWriter xtw, string path)
		{
			CmsApplicationContext ctx = new CmsApplicationContext();
			ctx.AuthenticateAsCurrentUser(PublishingMode.Update);

			// Get the gallery
			TemplateGallery g = (TemplateGallery) ctx.Searches.GetByPath(path);
			if (g == null) throw new Exception(
				string.Format("Unable to locate Template Gallery: {0}", path));

			// Write the gallery
			xtw.WriteStartElement("TemplateGallery");
			xtw.WriteAttributeString("Name", g.Name);
			WriteElement(xtw, "CreatedBy", g.CreatedBy.ToString());
			WriteElement(xtw, "CreatedDate", g.CreatedDate.ToString());
			WriteElement(xtw, "Description", g.Description);
			WriteElement(xtw, "Guid", g.Guid);
			WriteElement(xtw, "IsDeleted", g.IsDeleted.ToString());
			WriteElement(xtw, "IsRoot", g.IsRoot.ToString());
			WriteElement(xtw, "IsWorkingRevision", g.IsWorkingRevision.ToString());
			WriteElement(xtw, "LastModifiedBy", g.LastModifiedBy.ToString());
			WriteElement(xtw, "LastModifiedDate", g.LastModifiedDate.ToString());
			WriteElement(xtw, "OwnedBy", g.OwnedBy.ToString());
			if (g.Parent != null) WriteElement(xtw, "Parent", g.Parent.Name);
			WriteElement(xtw, "Path", g.Path);
			WriteElement(xtw, "RevisionDate", g.RevisionDate.ToString());
			if (g.TemplateGalleries.Count > 0) WriteTemplateGalleries(xtw, g);
			if (g.Templates.Count > 0) WriteTemplates(xtw, g);
			xtw.WriteEndElement();
		}

		#region WriteGallery helpers

		private static void WriteTemplateGalleries(XmlTextWriter xtw, TemplateGallery g)
		{
			xtw.WriteStartElement("TemplateGalleries");

			foreach (TemplateGallery tg in g.TemplateGalleries)
				WriteGallery(xtw, tg.Path);

			xtw.WriteEndElement();
		}

		private static void WriteTemplates(XmlTextWriter xtw, TemplateGallery g)
		{
			xtw.WriteStartElement("Templates");
			
			foreach (Template t in g.Templates)
				WriteTemplate(xtw, t.Path);
			
			xtw.WriteEndElement();
		}

		#endregion

		private static void WriteTemplate(XmlTextWriter xtw, string path)
		{
			CmsApplicationContext ctx = new CmsApplicationContext();
			ctx.AuthenticateAsCurrentUser(PublishingMode.Update);

			// Get the template
			Template t = (Template) ctx.Searches.GetByPath(path);
			if (t == null) throw new Exception(
				string.Format("Unable to locate Template: {0}", path));

			// Write as XML
			xtw.WriteStartElement("Template");
			xtw.WriteAttributeString("Name", t.Name);
			WriteElement(xtw, "Charset", t.Charset);
			WriteElement(xtw, "CodePage", t.CodePage.ToString());
			if (t.ConnectedTemplates.Count > 0) WriteConnectedTemplates(xtw, t);
			WriteElement(xtw, "CreatedBy", t.CreatedBy.ToString());
			WriteElement(xtw, "CreatedDate", t.CreatedDate.ToString());
			if (t.CustomPropertyDefinitions.Count > 0) WriteCustomProperties(xtw, t);
			WriteElement(xtw, "Description", t.Description);
			WriteElement(xtw, "Guid", t.Guid.ToString());
			WriteElement(xtw, "HasInaccessibleConnectedTemplates", t.HasInaccessibleConnectedTemplates.ToString());
			WriteElement(xtw, "IsConnected", t.IsConnected.ToString());
			WriteElement(xtw, "IsDeleted", t.IsDeleted.ToString());
			WriteElement(xtw, "IsWorkingRevision", t.IsWorkingRevision.ToString());
			WriteElement(xtw, "LastModifiedBy", t.LastModifiedBy.ToString());
			WriteElement(xtw, "LastModifiedDate", t.LastModifiedDate.ToString());
			WriteElement(xtw, "OwnedBy", t.OwnedBy.ToString());
			if (t.Parent != null) WriteElement(xtw, "Parent", t.Parent.Name);
			WriteElement(xtw, "Path", t.Path);
			if (t.PlaceholderDefinitions.Count > 0) WritePlaceholders(xtw, t);
			WriteElement(xtw, "RevisionDate", t.RevisionDate.ToString());
			WriteElement(xtw, "SourceFile", t.SourceFile);
			WriteElement(xtw, "State", t.State.ToString());
			xtw.WriteEndElement();
		}

		#region WriteTemplate helpers

		private static void WriteConnectedTemplates(XmlTextWriter xtw, Template template)
		{
			TemplateCollection tc = template.ConnectedTemplates;

			xtw.WriteStartElement("ConnectedTemplates");

			foreach (Template t in tc)
			{
				xtw.WriteStartElement("ConnectedTemplate");
				xtw.WriteAttributeString("Name", t.Name);
				xtw.WriteAttributeString("Path", t.Path);
				xtw.WriteEndElement();
			}

			xtw.WriteEndElement();
		}

		private static void WriteCustomProperties(XmlTextWriter xtw, Template template)
		{
			CustomPropertyDefinitionCollection cpdc = 
				template.CustomPropertyDefinitions;

			xtw.WriteStartElement("CustomPropertyDefinitions");

			foreach (CustomPropertyDefinition cpd in cpdc)
			{
				xtw.WriteStartElement("CustomPropertyDefinition");
				xtw.WriteAttributeString("Name", cpd.Name);
				xtw.WriteAttributeString("DefaultValue", cpd.DefaultValue);
				if (cpd.AllowedValues.Count > 0) WriteAllowedValues(xtw, cpd);
				xtw.WriteEndElement();
			}

			xtw.WriteEndElement();
		}

		private static void WriteAllowedValues(XmlTextWriter xtw, CustomPropertyDefinition cpd)
		{
			xtw.WriteStartElement("AllowedValues");

			foreach (string av in cpd.AllowedValues)
			{
				xtw.WriteStartElement("AllowedValue");
				xtw.WriteAttributeString("Value", av);
				xtw.WriteEndElement();
			}

			xtw.WriteEndElement();
		}

		private static void WritePlaceholders(XmlTextWriter xtw, Template template)
		{
			xtw.WriteStartElement("PlaceholderDefinitions");

			foreach (PlaceholderDefinition pd in template.PlaceholderDefinitions)
			{
				if (pd is AttachmentPlaceholderDefinition) 
					WriteAttachmentPlaceholderDefinition(xtw, pd as AttachmentPlaceholderDefinition);
				else if (pd is HtmlPlaceholderDefinition) 
					WriteHtmlPlaceholderDefinition(xtw, pd as HtmlPlaceholderDefinition);
				else if (pd is ImagePlaceholderDefinition)
					WriteImagePlaceholderDefinition(xtw, pd as ImagePlaceholderDefinition);
				else if (pd is XmlPlaceholderDefinition)
					WriteXmlPlaceholderDefinition(xtw, pd as XmlPlaceholderDefinition);
				else
					WritePlaceholderDefinition(xtw, pd);
			}

			xtw.WriteEndElement();
		}

		private static void WritePlaceholderDefinition(XmlTextWriter xtw, PlaceholderDefinition pd)
		{
			xtw.WriteStartElement("PlaceholderDefinition");
			xtw.WriteAttributeString("Name", pd.Name);
			WriteElement(xtw, "Description", pd.Description);
			WriteElement(xtw, "IsReadOnly", pd.IsReadOnly.ToString());
			WriteElement(xtw, "Type", pd.GetType().ToString());
			xtw.WriteEndElement();
		}

		private static void WriteAttachmentPlaceholderDefinition(XmlTextWriter xtw, AttachmentPlaceholderDefinition pd)
		{
			if (pd is OfficeAttachmentPlaceholderDefinition)
				xtw.WriteStartElement("OfficeAttachmentPlaceholderDefinition");
			else
				xtw.WriteStartElement("AttachmentPlaceholderDefinition");

			xtw.WriteAttributeString("Name", pd.Name);
			WriteElement(xtw, "Description", pd.Description);
			WriteElement(xtw, "IsReadOnly", pd.IsReadOnly.ToString());
			WriteElement(xtw, "MustUseResourceGallery", pd.MustUseResourceGallery.ToString());
			WriteElement(xtw, "UseGeneratedIcon", pd.UseGeneratedIcon.ToString());
			xtw.WriteEndElement();
		}

		private static void WriteHtmlPlaceholderDefinition(XmlTextWriter xtw, HtmlPlaceholderDefinition pd)
		{
			if (pd is OfficeHtmlPlaceholderDefinition)
                xtw.WriteStartElement("OfficeHtmlPlaceholderDefinition");
			else
				xtw.WriteStartElement("HtmlPlaceholderDefinition");

			xtw.WriteAttributeString("Name", pd.Name);
			WriteElement(xtw, "AllowAttachments", pd.AllowAttachments.ToString());
			WriteElement(xtw, "AllowHyperlinks", pd.AllowHyperlinks.ToString());
			WriteElement(xtw, "AllowImages", pd.AllowImages.ToString());
			WriteElement(xtw, "AllowLineBreaks", pd.AllowLineBreaks.ToString());
			WriteElement(xtw, "Description", pd.Description);
			WriteElement(xtw, "Formatting", pd.Formatting.ToString());
			WriteElement(xtw, "IsReadOnly", pd.IsReadOnly.ToString());
			WriteElement(xtw, "MustUseResourceGallery", pd.MustUseResourceGallery.ToString());
			WriteElement(xtw, "UseGeneratedIcon", pd.UseGeneratedIcon.ToString());
			xtw.WriteEndElement();
		}

		private static void WriteImagePlaceholderDefinition(XmlTextWriter xtw, ImagePlaceholderDefinition pd)
		{
			xtw.WriteStartElement("ImagePlaceholderDefinition");
			xtw.WriteAttributeString("Name", pd.Name);
			WriteElement(xtw, "AllowHyperlinks", pd.AllowHyperlinks.ToString());
			WriteElement(xtw, "Description", pd.Description);
			WriteElement(xtw, "IsReadOnly", pd.IsReadOnly.ToString());
			WriteElement(xtw, "MustUserResourceGallery", pd.MustUseResourceGallery.ToString());
			xtw.WriteEndElement();
		}

		private static void WriteXmlPlaceholderDefinition(XmlTextWriter xtw, XmlPlaceholderDefinition pd)
		{
			xtw.WriteStartElement("XmlPlaceholderDefinition");
			xtw.WriteAttributeString("Name", pd.Name);
			WriteElement(xtw, "CheckForValidity", pd.CheckForValidity.ToString());
			WriteElement(xtw, "CheckForWellFormedness", pd.CheckForWellFormedness.ToString());
			WriteElement(xtw, "Description", pd.Description);
			WriteElement(xtw, "IsReadOnly", pd.IsReadOnly.ToString());
			WriteElement(xtw, "ManageCmsUrls", pd.ManageCmsUrls.ToString());
			WriteElement(xtw, "XsdUrl", pd.XsdUrl);
			xtw.WriteEndElement();
		}

		#endregion
	}
}
